/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */

/* 
 * File:   main.cpp
 * Author: ever
 *
 * Created on 2018年5月12日, 下午5:47
 */

#include <cstdlib>



/*
 * 
 */
#include "KKSFile.h"
#include "TL_Option.h"
#include "TL_Common.h"
#include "TL_Datetime.h"

#include "KKSManger.h"

#include <vector>
#include <TL_Singleton.h>

using namespace std;
using namespace tidp;

void usage() {
    cerr << "--out    开启导入数据功能\n"
            "         --in     输入数据文件名，如果没有将从标准输入读取\n"
            "                  数据格式为: 测点|时间|值|健康度\n"
            "         --kidx   索引文件，必须保证索引一致。通常保存在 ${history}/kks/kks.idx\n\n"
            "--kfile  指定 kks 数据文件，用于查看文件存储的数据\n"
            "--kid    查看指定 kid的块信息\n"
            "--kks    查看指定测点名的块信息，需要和--kidx配合使用\n"
            "--rid    查看指定row的数据，row id\n"
            "\n"
            "--export 数据导出模式; 必须制定 --kidx --kfile参数\n"
            "--ktype  测点类型映射文件，文件格式： 测点ID,测点名,测点类型"
         << endl;
    exit(-1);
}


int _fd = -1;

/**
 * 某个kks的链信息
 * @param kfile
 */
void kks_chain(KKSFile &kfile, u32_t id) {
    KKSFile::KKS_Meta *m = kfile.getMeta(id);
    if (!m) {
        cerr << "kid:" << id << " is not found." << endl;
        return;
    }
    cerr << "kid:" << id << ",first:" << m->offset_first << ",current:" << m->offset_current << ",chains:" << endl;
    KKSFile::KKS_ROW_Head rhead;
    off_t row_off = m->offset_first;
    TL_Datetime dt;
    while (row_off > 0 && row_off <= m->offset_current) {
        pread(_fd, &rhead, sizeof(KKSFile::KKS_ROW_Head), row_off);

        cerr << "    rid:[" << row_off << "," << rhead.offset_next << "],tm:[" << rhead.start_tm << "," << rhead.tail_tm << ",\"";
        dt.setTime(rhead.start_tm);
        cerr << dt.toString() << ",";
        dt.setTime(rhead.tail_tm);
        cerr << dt.toString() << "\"],count:" << rhead.count << endl;
        row_off = rhead.offset_next;
    }
    //cerr << " -- end kid :" << id << endl;
}

/**
 * 显示具体某个row的所有数据
 * @param kfile
 * @param off
 */
void row_data(KKSFile &kfile, off_t off) {
    vector<char> buffer;
    buffer.reserve(KSS_ROW_LENGTH);
    char *addr = &buffer[0];
    pread(_fd, addr, KSS_ROW_LENGTH, off);
    KKSFile::KKS_ROW_Head *rhead = (KKSFile::KKS_ROW_Head *) addr;
    KKSFile::KKS_Value *value = (KKSFile::KKS_Value *) (addr + sizeof(KKSFile::KKS_ROW_Head));
    //KKSFile::KKS_Float_Value * value = (KKSFile::KKS_Float_Value *)(addr + sizeof (KKSFile::KKS_ROW_Head));

    cerr << "start_time:" << rhead->start_tm << ",tail_time:" << rhead->tail_tm << ",count:" << rhead->count << endl;
    short ddtm = 0;
    u32_t ii = 0;
    u32_t last_tm = rhead->start_tm;
    int last_delta = 0;
    KKSFile::KKS_Float_Value *fvalue = nullptr;
    TL_Datetime dt;

    for (u32_t i = 0; i < rhead->count; ++i) {
        fvalue = (KKSFile::KKS_Float_Value *) value;
        if (value->delta_delta_tm & 0x80) {
            KKSFile::KKS_Value *value0 = value;
            ++value;
            fvalue = (KKSFile::KKS_Float_Value *) value;
            last_tm = value0->value;
            last_delta = 0;
            ddtm = 0;
            //cerr << i << ",MatchPoint(" << value0->value << "," << value->value << ") " << endl;
            //++value;
            ++i;
            //continue;
        } else if (value->delta_delta_tm & 0x40) {
            ddtm = -(value->delta_delta_tm & 0x3f);
        } else {
            ddtm = value->delta_delta_tm;
        }
        last_delta += ddtm; // + last_delta);
        last_tm += last_delta;
        //delta += ddtm;
        ++ii;
        dt.setTime(last_tm);
        cerr << ii << "\t,(\t" << ddtm << "|\t" << last_delta << "|\t" << last_tm << "|" << dt.toString() << ",\t"
             << value->value << "|\t" << fvalue->value << "," << fvalue->quality << ") " << endl;
        ++value;
    }
    cerr << endl;

}

void import_data(FILE *in, const string &outfile) {
#define CAL_POS do{\
    p[0] = linebuf;\
    int i = 1;\
    while(i < 5 && *p[0] != '\0'){\
        if(*p[0] == '|'){\
            *p[0] = '\0';\
            p[i] = ++p[0];\
            ++i;\
        }else{\
            ++p[0];\
        }\
    }\
    p[0] = linebuf;\
    tm = atoll(p[1]);\
    *fp = atof(p[2]);\
}while(0)

    enum {
        bufflen = 1024
    };
    vector<char> line(bufflen);
    //line.reserve(2046);
    ssize_t linelen;
    char *linebuf = &line[0];
    char *endofline;
    char *p[5];

    //获取首行数据，获得时间
    endofline = fgets(linebuf, bufflen, in);
    if (endofline == NULL) {
        cerr << "input file is empty!" << endl;
        return;
    }
    time_t tm;
    u32_t vl;
    float *fp = (float *) &vl;
    KKSManger *km = KKSManger::getInstance();
    CAL_POS;
    time_t first_tm = tm;
    std::shared_ptr<KKSFile> kfile_ptr = km->getKKSFile(first_tm, outfile);
    time_t day = (first_tm + BEIJING_TIME) / ONE_DAY_SECONDS * ONE_DAY_SECONDS;
    KKSIndex &k_index = km->getKKSIndex();
    u32_t kid = 0;
    if (k_index.empty()) {
        kid = atoi(p[0]);
    } else {
        kid = k_index.getKKSId(p[0]);
    }
    if (kid == 0) {
        cerr << "kks:" << p[0] << " index is zero : " << k_index.size() << endl;
        return;
    }
    TL_Datetime dt(first_tm);
    cerr << "staring import data. first tm " << first_tm << "," << dt.toString() << endl;
    kfile_ptr->addKKS(kid, tm, vl, *p[3] | 0x80);
    int count = 1;
    while ((endofline = fgets(linebuf, bufflen, in)) != NULL) {
        kfile_ptr->reduceRowsByCount(20000);
        CAL_POS;
        if ((tm + BEIJING_TIME) / ONE_DAY_SECONDS * ONE_DAY_SECONDS != day) {
            cerr << "not same day date stop. first tm is " << dt.toString();
            dt.setTime(tm);
            cerr << ", curr is " << dt.toString() << endl;
            return;
        }

        kid = k_index.getKKSId(p[0]);
        if (kid == 0) {
            cerr << "kks:" << p[0] << " index is zero." << endl;
            return;
        }

        kfile_ptr->addKKS(kid, tm, vl, *p[3]);
        //cerr << "addKKS:" << kid << "|" << tm << "|" << vl << "|" << (int) *p[3] << endl;
        ++count;
    }
    cerr << "import success, count : " << count << endl;
}

void export_data(const string &export_file, const string &kks_type_file) {
    //type
    vector<char> buffer;
    buffer.reserve(KSS_ROW_LENGTH);
    char *addr = &buffer[0];
    map<u32_t, char> kks_types;
    auto fp = fopen(kks_type_file.c_str(), "r");
    if (fp == nullptr) {
        cerr << "can't open " << kks_type_file << endl;
        return;
    }
    auto r = fgets(addr, KSS_ROW_LENGTH, fp);
    while (r != nullptr) {
        int j = 0;
        for (int i = 0; i < KSS_ROW_LENGTH; ++i) {
            if (addr[i] == ',') {
                ++j;
                addr[i] = 0;
                if (j == 2) {
                    kks_types[atoll(addr)] = addr[i + 1];
                    break;
                }
            }
        }
        r = fgets(addr, KSS_ROW_LENGTH, fp);
    }

    //data
    KKSFile kfile;
    kfile.attach(export_file);
    auto all_kks = kfile.getAllKKS();
    //cerr << "all kid size:" << all_kks.size() << endl;
    int kks_type = 0;
    for (auto kid : all_kks) {
        auto row_offset = kfile.getRowOffset(kid);
        for (auto off:row_offset) {
            kfile.getRowData(off, buffer);
            KKSFile::KKS_Float_Value *fvalue = nullptr;
            //for (int i = 0; i < rows.size(); ++i) {


            KKSFile::KKS_ROW_Head *rhead = (KKSFile::KKS_ROW_Head *) addr;
            KKSFile::KKS_Value *value = (KKSFile::KKS_Value *) (addr + sizeof(KKSFile::KKS_ROW_Head));
            //ddtm = 0;
            int last_delta = 0;
            u32_t last_tm = rhead->start_tm;
            short ddtm = 0;
            for (u32_t j = 0; j < rhead->count; ++j) {
                fvalue = (KKSFile::KKS_Float_Value *) value;
                if (value->delta_delta_tm & 0x80) {
                    KKSFile::KKS_Value *value0 = value;
                    ++value;
                    last_tm = value0->value;
                    last_delta = 0;
                    ddtm = 0;
                    fvalue = (KKSFile::KKS_Float_Value *) value;
                    ++j;
                    //continue;
                } else if (value->delta_delta_tm & 0x40) {
                    ddtm = -(value->delta_delta_tm & 0x3f);
                } else {
                    ddtm = value->delta_delta_tm;
                }
                last_delta += ddtm; // + last_delta);
                last_tm += last_delta;
                //delta += ddtm;
                //++ii;
                //cerr << ii << ",(" << ddtm << "|" << last_tm << "~" << last_delta << "," << value->value << "|" << fvalue->value << ") " << endl;
                ++value;
                cout << kid << "|" << kks_types[kid] << "|" << last_tm << "|" << fvalue->value << "|" << fvalue->quality << endl;
            }
        };
    }
}

/**
 * 概要信息
 * @param kfile
 */
void summary(KKSFile &kfile) {
    KKSFile::KKS_File_Head *head = kfile.getKKSFileHead();
    cerr << "kks's file   : " << kfile.getKKSFileName() << endl;
    cerr << "version      : " << head->major << "." << head->minor << endl;
    cerr << "kks count    : " << head->kks_count << endl;
    cerr << "kks capcity  : " << head->kks_max_count - head->kks_count << endl;

    /* 数量太大
    u32_t id;
    KKSFile::KKS_Meta * meta = kfile.getMeta(0);
    for (u32_t n = 0; n < head->kks_count; ++n) {
        kks_chain(kfile, meta->kks_id);
        ++meta;
    }
     */
}

int main(int argc, char **argv) {
    TL_Option opt;
    opt.decode(argc, argv);

    if (opt.hasParam("export")) {
        auto kfile = opt.getValue("kfile");
        auto ktype = opt.getValue("ktype");

        if (!TL_Common::isFileExistEx(kfile)) {
            cerr << "kks'file : " << kfile << " not exist." << endl;
            exit(-2);
        }

        if (!TL_Common::isFileExistEx(ktype)) {
            cerr << "kks type file : " << ktype << " not exist." << endl;
            exit(-2);
        }


        export_data(kfile, ktype);
        return 0;
    }


    string outfile = opt.getValue("out");
    if (!outfile.empty()) {
        //实现数据导入逻辑
        KKSManger *km = KKSManger::getInstance();
        string kks_index = opt.getValue("kidx");
        if (!kks_index.empty()) {
            km->getKKSIndex().attach(kks_index);
        } else {
            cerr << "未指定 --kidx 参数； 数据必须以测点ID" << endl;
        }
        FILE *in = nullptr;
        string infile = opt.getValue("in");
        if (infile.empty()) {
            in = stdin;
        } else {
            in = fopen(infile.c_str(), "r");
            if (!in) {
                cerr << "open input file : " << infile << " failed." << endl;
                exit(-1);
            }
        }
        import_data(in, outfile);
        km->saveIndex();
        return 0;
    }

    string kfile_name = opt.getValue("kfile");
    if (kfile_name.empty()) {
        cerr << "未指定kks 数据文件。" << endl;
        usage();
    }

    if (!TL_Common::isFileExistEx(kfile_name)) {
        cerr << "kks'file : " << kfile_name << " not exist." << endl;
        exit(-2);
    }

    _fd = open(kfile_name.c_str(), O_EXCL | O_RDWR, 0666);
    if (_fd < 0) {
        cerr << "kks'file : " << kfile_name << " can not be opened." << endl;
        exit(-2);
    }
    KKSFile kfile;
    kfile.attach(kfile_name);
    //kfile.test();
    summary(kfile);

    string kid = opt.getValue("kid");
    if (kid.size() > 0) {
        u32_t kidid = atoll(kid.c_str());
        kks_chain(kfile, kidid);
    }

    string row_off = opt.getValue("rid");
    int method = 1; //计算方式
    int column = 10; //没行显示个数
    if (!row_off.empty()) {
        off_t off = atoll(row_off.c_str());
        row_data(kfile, off);
    }
    if (_fd) close(_fd);
    return 0;
}

